package solutions.ferpa.ztest.intf;

import br.com.telescope.adsrt.dom.DomFlag;
import br.com.telescope.adsrt.dom.DomId;
import br.com.telescope.adsrt.dom.DomInteger;
import br.com.telescope.adsrt.dom.DomOpcao;
import br.com.telescope.adsrt.dom.DomSourceCode;
import br.com.telescope.adsrt.dom.DomText;
import br.com.telescope.adsrt.dom.DomTextLong;
import br.com.telescope.adsrt.dom.DomUpper;
import br.com.telescope.adsrt.lib.*;
import br.com.telescope.t2.ServiceModel;
import br.com.telescope.t2.exception.ApplicationRuntimeException;
import br.com.telescope.t2.feature.CustomFeature;
import br.com.telescope.t2.feature.DeleteFeature;
import br.com.telescope.t2.feature.InsertFeature;
import br.com.telescope.t2.feature.ListFeature;
import br.com.telescope.t2.feature.MethodFeature;
import br.com.telescope.t2.feature.QueryFeature;
import br.com.telescope.t2.feature.UpdateFeature;
import br.com.telescope.util.JdbcUtil;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Hashtable;
import solutions.ferpa.ztest.ent.*;

/**
 * Executor de SQL e DML.
 * <p>
 * Interface que permite ao usuário executar consultas e DMLs livremente 
 * no banco de dados.
 * <P>
 * O usuário pode escrever vários comandos em sequencia em uma mesma 
 * entrada. Estes comandos serão executados em uma transação e 
 * automaticamente comitados no final.
 * <P>
 * 
 * <P>
 * O resultado dos comandos SELECT executados será através de uma 
 * tabela.
 * <P>
 * 
 * <P>
 * Para os demais comandos de DML ou DDL, apenas o número de registros 
 * processados será informado.
 * <P>
 * 
 * <P>
 * Além dos comandos tradicionais de SQL, DML e DDL, os seguintes 
 * comandos são reconhecidos:
 * <P>
 * *DESC tabela - Apresenta as informações sobre a tabela informada 
 * (colunas e tipagem de dados)
 * <P>
 * *COMMIT - Executa um commit na conexão.
 * <P>
 * *ROLLBACK - Executa um rollback na conexão.
 * <P>
 * *SUM col1 col2 ... - Estabelece uma lista de colunas que deseja que 
 * sejam totalizadas no final.
 * <P>
 * 
 * <P>
 * Todos os DML e DDL registrados são registrados no log de replicação, 
 * ou seja, são automaticamente replicados para os outros servidores 
 * conectados à base de replicação.
 * <P>
 * 
 * <P>
 * 
 * <P>
 * Telescope origin: RTDBUT.UTL_DB_SQL_2
 * <P>
 * Base entity: RTSYSINF.OPERACOES_SISTEMAS
 * <P>
 * @author TELESCOPE - Build task T2 Web Interface Generator
 * <P>
 * @version 0.1
 */
public class UtlDbSql2 extends ServiceModel {
    
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    public static final String GLOBAL_NAME = "RTDBUT.UTL_DB_SQL_2";
    
    @Override
    public String globalName() {
        return GLOBAL_NAME;
    }
    
    {
        setTitle("Executor de SQL e DML");
    }
    
    protected EntOperacoesSistemas base;
    protected EntInterfacesSistemas intf;
    protected EntOperacoesSistemas rec; // NEED_TJW1_COMPATIBILITY
    
    // Record items
    private DomId aId;
    private DomUpper aOperacao;
    private DomText aTitulo;
    private DomTextLong aDescricao;
    private DomId aInterfaceId;
    private DomOpcao aPoliticaAcesso;
    private DomOpcao aTipo;
    private DomUpper aNomeInterface;
    private DomText aTituloInterface;
    private DomInteger aRegistros;
    private DomFlag aReplicar;
    private DomSourceCode aCommand;
    
    // Query items
    private DomTextLong qDescricao;
    private DomText qTitulo;
    
    @Override
    public String getBaseSql() {
        return "select BASE.*"
                + " , INTF.NOME as NOME_INTERFACE"
                + " , INTF.TITULO as TITULO_INTERFACE"
                + " , INTF.ID as Z_INTF_ID"
                + " from OPERACOES_SISTEMAS BASE"
                + " left join INTERFACES_SISTEMAS INTF on INTF.ID = BASE.INTERFACE_ID";
    }
    
    @Override
    public void initAttributes() {
        
        aId = new DomId();
        aId.context(ctx).name("ID").label("Id");
        
        aOperacao = new DomUpper();
        aOperacao.context(ctx).name("OPERACAO").label("Operação");
        
        aTitulo = new DomText();
        aTitulo.context(ctx).name("TITULO").label("Título");
        
        aDescricao = new DomTextLong();
        aDescricao.context(ctx).name("DESCRICAO").label("Descrição");
        
        aInterfaceId = new DomId();
        aInterfaceId.context(ctx).name("INTERFACE_ID").label("Interface do sistema");
        
        aPoliticaAcesso = new DomOpcao();
        aPoliticaAcesso.context(ctx).name("POLITICA_ACESSO").label("Política de acesso").group(true);
        aPoliticaAcesso.addOption("P", "Público");
        aPoliticaAcesso.addOption("R", "Restrito");
        aPoliticaAcesso.addOption("U", "Usuário");
        aPoliticaAcesso.setDefaultValue("R");
        
        aTipo = new DomOpcao();
        aTipo.context(ctx).name("TIPO").label("Tipo").group(true);
        aTipo.addOption("I", "Implementação custom");
        aTipo.addOption("R", "Report");
        aTipo.addOption("S", "Service");
        aTipo.addOption("U", "User interface");
        aTipo.setDefaultValue("U");
        
        aNomeInterface = new DomUpper();
        aNomeInterface.context(ctx).name("NOME_INTERFACE").label("Nome da Interface");
        aNomeInterface.setListOfValuesQuery(createQuery()
                .select("*")
                .from("(SELECT NOME as valor"
                + " , NOME as pesquisar"
                + " , NOME as descricao"
                + " from INTERFACES_SISTEMAS INTF)", "q"));
        aNomeInterface.setAutocompleteLength(1);
        
        aTituloInterface = new DomText();
        aTituloInterface.context(ctx).name("TITULO_INTERFACE").label("Título");
        aTituloInterface.setListOfValuesQuery(createQuery()
                .select("*")
                .from("(SELECT TITULO as valor"
                + " , TITULO as pesquisar"
                + " , TITULO as descricao"
                + " from INTERFACES_SISTEMAS INTF)", "q"));
        aTituloInterface.setAutocompleteLength(1);
        
        aRegistros = new DomInteger();
        aRegistros.context(ctx).name("REGISTROS").label("Registros").unbound(true);
        
        aReplicar = new DomFlag();
        aReplicar.context(ctx).name("REPLICAR").label("Replicar").unbound(true);
        
        aCommand = new DomSourceCode();
        aCommand.context(ctx).name("COMMAND").label("Command").height(8).unbound(true);
        
        
        qDescricao = new DomTextLong();
        qDescricao.context(ctx).name("DESCRICAO").label("Descrição");
        
        qTitulo = new DomText();
        qTitulo.context(ctx).name("TITULO").label("Título");
    }
    
    @Override
    public String getQueryPkColumn() {
        return "BASE.ID";
    }
    
    @Override
    public void setId(String value) {
        this.aId.setValue(value);
    }
    
    @Override
    public EntOperacoesSistemas getRecord() {
        if (base == null) {
            base = new EntOperacoesSistemas(ctx);
            rec = base;
        }
        return this.base;
    }
    
    @Override
    public String getContextName() {
        return aTitulo.toText();
    }
    
    @Override
    public String getDefaultOrderBy() {
        return "TITULO";
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="INSERT">
    public class FInsert extends InsertFeature {
        
        @Override
        public void initForm() {
            addField(aRegistros, "opt");
            addField(aReplicar, "opt");
            addField(aTitulo, "opt");
            addField(aDescricao, "opt");
            addField(aInterfaceId, "opt");
        }
        
        @Override
        public void onAction() {
            addSubmit(getFeature("EXECUTAR_COMANDOS")).label("Executar comandos").shortCutKey("F3").icon("salvar.gif");
        }
        
        {
            setFeatureName("INSERT");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="HOME">
    public class FHome extends CustomFeature {
        
        @Override
        public void initForm() {
            aId.setOptional(false);
            addField(aId, "rqd");
            aOperacao.setOptional(false);
            addField(aOperacao, "rqd");
            aTitulo.setOptional(false);
            addField(aTitulo, "rqd");
            aDescricao.setEditable(false);
            addField(aDescricao, "view");
            aPoliticaAcesso.setEditable(false);
            addField(aPoliticaAcesso, "view");
            aTipo.setOptional(false);
            addField(aTipo, "rqd");
        }
        
        @Override
        public void posDisplay() {
            try {
                inicializaReplicacao();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("HOME");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="CUSTOM">
    public class FCustom extends CustomFeature {
        
        @Override
        public void initForm() {
            addField(aRegistros, "opt");
            addField(aReplicar, "opt");
            addField(aCommand, "opt");
            aId.setOptional(false);
            addField(aId, "rqd");
            aOperacao.setOptional(false);
            addField(aOperacao, "rqd");
            aTitulo.setOptional(false);
            addField(aTitulo, "rqd");
            addField(aDescricao, "opt");
            addField(aPoliticaAcesso, "opt");
            aTipo.setOptional(false);
            addField(aTipo, "rqd");
        }
        
        @Override
        public void onAction() {
            addSubmit(getFeature("EXECUTAR_COMANDOS")).label("Executar comandos").shortCutKey("F3").icon("salvar.gif");
        }
        
        {
            setFeatureName("CUSTOM");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="QUERY">
    public class FQuery extends QueryFeature {
        
        @Override
        public void initForm() {
            addField(qTitulo, "opt");
            addField(qDescricao, "opt");
        }
        
        {
            setFeatureName("QUERY");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="LIST">
    public class FList extends ListFeature {
        
        @Override
        public void initForm() {
            addField(aTitulo, "view");
        }
        
        {
            setFeatureName("LIST");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="UPDATE">
    public class FUpdate extends UpdateFeature {
        
        @Override
        public void initForm() {
            addField(aRegistros, "opt");
            addField(aReplicar, "opt");
            addField(aTitulo, "opt");
            addField(aDescricao, "opt");
            addField(aInterfaceId, "opt");
        }
        
        @Override
        public void onAction() {
            addSubmit(getFeature("EXECUTAR_COMANDOS")).label("Executar comandos").shortCutKey("F3").icon("salvar.gif");
        }
        
        {
            setFeatureName("UPDATE");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="DELETE">
    public class FDelete extends DeleteFeature {
        
        @Override
        public void initForm() {
            addField(aTitulo, "view");
        }
        
        {
            setFeatureName("DELETE");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="EXECUTAR_COMANDOS">
    public class FExecutarComandos extends MethodFeature {
        
        @Override
        public void run() {
            preRun();
            try {
                executarComandos();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("EXECUTAR_COMANDOS");
            setTitle("Executar comandos");
            setShortCutKey("F3");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="initFeatures">
    
    @Override
    protected void initFeatures() {
        addFeature(new FInsert());
        addFeature(new FHome());
        addFeature(new FCustom());
        addFeature(new FQuery());
        addFeature(new FList());
        addFeature(new FUpdate());
        addFeature(new FDelete());
        addFeature(new FExecutarComandos());
        super.initFeatures();
    }
    
    @Override
    public String getDefaultFeature() {
        return "INSERT";
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Getters and Setters">
    
    @Override
    public Long getId() {
        return aId.getValue();
    }
    
    public void setId(Long value) {
        this.aId.setValue(value);
    }
    
    public String getOperacao() {
        return aOperacao.getValue();
    }
    
    public void setOperacao(String value) {
        this.aOperacao.setValue(value);
    }
    
    public String getTitulo() {
        return aTitulo.getValue();
    }
    
    public void setTitulo(String value) {
        this.aTitulo.setValue(value);
    }
    
    public String getDescricao() {
        return aDescricao.getValue();
    }
    
    public void setDescricao(String value) {
        this.aDescricao.setValue(value);
    }
    
    public Long getInterfaceId() {
        return aInterfaceId.getValue();
    }
    
    public void setInterfaceId(Long value) {
        this.aInterfaceId.setValue(value);
    }
    
    public String getPoliticaAcesso() {
        return aPoliticaAcesso.getValue();
    }
    
    public void setPoliticaAcesso(String value) {
        this.aPoliticaAcesso.setValue(value);
    }
    
    public String getTipo() {
        return aTipo.getValue();
    }
    
    public void setTipo(String value) {
        this.aTipo.setValue(value);
    }
    
    public String getNomeInterface() {
        return aNomeInterface.getValue();
    }
    
    public void setNomeInterface(String value) {
        this.aNomeInterface.setValue(value);
    }
    
    public String getTituloInterface() {
        return aTituloInterface.getValue();
    }
    
    public void setTituloInterface(String value) {
        this.aTituloInterface.setValue(value);
    }
    
    public Integer getRegistros() {
        return aRegistros.getValue();
    }
    
    public void setRegistros(Integer value) {
        this.aRegistros.setValue(value);
    }
    
    public String getReplicar() {
        return aReplicar.getValue();
    }
    
    public void setReplicar(String value) {
        this.aReplicar.setValue(value);
    }
    
    public String getCommand() {
        return aCommand.getValue();
    }
    
    public void setCommand(String value) {
        this.aCommand.setValue(value);
    }
    public String getQueryTitulo() {
        return qTitulo.getQueryValue();
    }
    
    public void setQueryTitulo(String value) {
        this.qTitulo.setQueryValue(value);
    }
    public String getQueryDescricao() {
        return qDescricao.getQueryValue();
    }
    
    public void setQueryDescricao(String value) {
        this.qDescricao.setQueryValue(value);
    }
    // </editor-fold>
    
    /**
     * Declarações.
     * <p>
     * Objetos de instância.
     * <P>
     */
    // <user-code id="675635" parentId="675634" hashcode="-22968534" length="88">
    private Hashtable<String, Double> totalSumColumns = new Hashtable<String, Double>();
    
    
    // </user-code>
    
    /**
     * Executar comandos.
     * <p>
     * Executa os comandos informados no campo de comando.
     * <P>
     */
    public void executarComandos() throws AdsException {
        // <user-code id="806888" parentId="675663" hashcode="1caa72cf" length="738">
        aCommand.getHtmlParameter();
        aReplicar.getHtmlParameter();
        aRegistros.getHtmlParameter();
        
        doCustom();
        try {
            ctx.getConnection().setAutoCommit(false);
        } catch (SQLException ex) {
            throw new AdsException(ex);
        }
        String text = aCommand.getValue().trim();
        if (!text.endsWith(";")) text = text + ";";
        String[] cmds = text.split("\n");
        StringBuilder cmd = new StringBuilder();
        for (String line : cmds) {
            cmd.append(" " + line);
            if (line.trim().endsWith(";")) {
                ctx.p("<p> <code>" + cmd.toString() + "</code>");
                execute(cmd.toString().trim());
                cmd = new StringBuilder();
            }
        }
        try {
            ctx.getConnection().commit();
        } catch (SQLException ex) {
            throw new AdsException(ex);
        }
        // </user-code>
    }
    
    /**
     * Execute.
     * <p>
     * Executa um comando específico.
     * <P>
     */
    private void execute(String command) throws AdsException {
        // <user-code id="806889" parentId="675660" hashcode="342f43a7" length="642">
        if (command.toUpperCase().startsWith("SELECT ")) {
            executeSelect(command);
        } else if (command.toUpperCase().startsWith("DESC ")) {
            executeDesc(command);
        } else if (command.toUpperCase().startsWith("SUM ")) {
            executeSum(command);
        } else if (command.equalsIgnoreCase("COMMIT")) {
            try {
                ctx.getConnection().commit();
            } catch (SQLException ex) {
                throw new AdsException(ex);
            }
        } else if (command.equalsIgnoreCase("ROLLBACK")) {
            try {
                ctx.getConnection().rollback();
            } catch (SQLException ex) {
                throw new AdsException(ex);
            }
        } else {
            executeDml(command);
        }
        // </user-code>
    }
    
    /**
     * Execute desc.
     * <p>
     * Apresenta uma descrição de uma tabela.
     * <P>
     */
    private void executeDesc(String command) throws AdsException {
        // <user-code id="806890" parentId="675654" hashcode="927b73c" length="1140">
        String entity = command.substring(5).trim().replaceAll(";", "");
        String sql = "select * from " + entity + " limit 1";
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = ctx.getConnection().prepareStatement(sql);
            rs = ps.executeQuery();
            ResultSetMetaData meta = rs.getMetaData();
            ctx.p("<table border=1>");
            ctx.p("<tr>");
            ctx.p("<th>Column</th>");
            ctx.p("<th>Data type</th>");
            ctx.p("<th>Precision</th>");
            ctx.p("<th>Scale</th>");
            ctx.p("<th>Required</th>");
            ctx.p("</tr>");
            for (int i = 1; i <= meta.getColumnCount(); i++) {
                ctx.p("<tr>");
                ctx.p("<td>" + meta.getColumnName(i) + "</td>");
                ctx.p("<td>" + meta.getColumnTypeName(i) + "</td>");
                ctx.p("<td>" + meta.getPrecision(i) + "</td>");
                ctx.p("<td>" + meta.getScale(i) + "</td>");
                ctx.p("<td align=center>" + ( meta.isNullable(i) == 1 ? " " : "*" ) + "</td>");
                ctx.p("</tr>");
            }
            ctx.p("</table>");
        } catch (SQLException ex) {
            ctx.p("<div style='color:red;'>" + ex.getMessage() + "</div>");
        } finally {
            JdbcUtil.close(ps, rs);
        }
        // </user-code>
    }
    
    /**
     * Execute DML.
     * <p>
     * Executa um comando DML
     */
    private void executeDml(String command) throws AdsException {
        // <user-code id="806891" parentId="675657" hashcode="456e9aef" length="442">
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = ctx.getConnection().prepareStatement(command);
            int count = ps.executeUpdate();
            if (aReplicar.getValue().equals("S")) {
                Entity.logDmlRecord(ctx, command);
            }
            ctx.p("<br>" + count + " records processed!");
        } catch (SQLException ex) {
            ctx.p("<div style='color:red;'>" + ex.getMessage() + "</div>");
        } finally {
            JdbcUtil.close(ps, rs);
        }
        // </user-code>
    }
    
    /**
     * Execute Select.
     * <p>
     * Executa um comando do tipo SELECT
     * <P>
     * Apresenta uma tabela com os dados resultantes.
     * <P>
     */
    private void executeSelect(String command) throws AdsException {
        // <user-code id="806892" parentId="675639" hashcode="57741b78" length="1761">
        // import br.com.telescope.util.JdbcUtil;
        int maxlines = aRegistros.getValue();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = ctx.getConnection().prepareStatement(command);
            rs = ps.executeQuery();
            ResultSetMetaData meta = rs.getMetaData();
            ctx.p("<table border=1>");
            ctx.p("<tr>");
            for (int i = 1; i <= meta.getColumnCount(); i++) {
                ctx.p("<th>" + meta.getColumnName(i) + "</th>");
            }
            ctx.p("</tr>");
            while (rs.next() && maxlines-- > 0) {
                ctx.p("<tr>");
                for (int i = 1; i <= meta.getColumnCount(); i++) {
                    ctx.p("<td>" + rs.getObject(i) + "</td>");
                }
                // Aplicar soma total
                if (totalSumColumns.size() > 0) {
                    for (int i = 1; i <= meta.getColumnCount(); i++) {
                        String name = meta.getColumnName(i);
                        Double sum = totalSumColumns.get(name);
                        if (sum != null) {
                            totalSumColumns.put(name, sum + rs.getDouble(i));
                        }
                    }
                }
                ctx.p("</tr>");
            }
            if (totalSumColumns.size() > 0) {
                ctx.p("<tr><th colspan=" + meta.getColumnCount() + "><b>Totais gerais</b></th></tr><tr>");
                for (int i = 1; i <= meta.getColumnCount(); i++) {
                    String name = meta.getColumnName(i);
                    Double sum = totalSumColumns.get(name);
                    if (sum != null) {
                        ctx.p("<td>" +  sum + "</td>");
                    } else {
                        ctx.p("<td></td>");
                    }
                }
                ctx.p("</tr>");
            }
            ctx.p("</table>");
        } catch (SQLException ex) {
            ctx.p("<div style='color:red;'>" + ex.getMessage() + "</div>");
        } finally {
            JdbcUtil.close(ps, rs);
        }
        // </user-code>
    }
    
    /**
     * Execute SUM.
     * <p>
     * Executa a interpretação do comando SUM.
     * <P>
     */
    private void executeSum(String command) throws AdsException {
        // <user-code id="806893" parentId="675636" hashcode="8c37aa0" length="402">
        if (command.endsWith(";")) command = command.substring(0, command.length()-1);
        totalSumColumns = new Hashtable<String, Double>();
        String[] parts = command.split(" ");
        String mode = "sum";
        for (String part : parts) {
            if (mode.equals("sum")) {
                // Salta a palavra sum
                mode = "cols";
            } else if (mode.equals("cols")) {
                totalSumColumns.put(part.trim(), 0d);
            }
        }
        // </user-code>
    }
    
    /**
     * Inicializa replicação.
     */
    private void inicializaReplicacao() throws AdsException {
        // <user-code id="806894" parentId="675648" hashcode="5b1f9642" length="66">
        aReplicar.setValue("S");
        aRegistros.setValue("100");
        doCustom();
        // </user-code>
    }
}
